#pragma once
#include <iostream>
#include <queue>
#include <unistd.h>
#include <pthread.h>

using namespace std;
int gcap = 5;
namespace BlockQueueModule
{
    template<typename T>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _q.size() == _cap;
        }
        bool IsEmpty()
        {
            return _q.empty();
        }
    public:

        BlockQueue(int cap = gcap) : _cap(cap), _cwait_num(0), _pwait_num(0)
        {
            pthread_mutex_init(&_mutex,nullptr);
            pthread_cond_init(&_productor_cond,nullptr);
            pthread_cond_init(&_consumer_cond,nullptr);
        }

        void Equeue(const T &in)
        {
            pthread_mutex_lock(&_mutex);
            // 你想放数据，就能放吗？？生产数据是有条件的
            // 我们如何判断是否需要等待，是否为满。判断属于访问临界区，所以等待必定在临界区内等待
            // 结论1：在临界区中等待是必然的(目前)
            while(IsFull()) // 5. 对条件进行伪唤醒，我们通常用while循环检测
            {
                _pwait_num++;
                // 2.等时，释放_mutex
                pthread_cond_wait(&_productor_cond,&_mutex); // wait的时候，必定是持有锁的！但是持有锁有问题，因为这把锁还要被消费者申请，所以要释放锁
                // 3.返回，线程被唤醒&&重新申请并持有锁(它会在临界区内醒来！！)
                _pwait_num--;
                
            }
            // 4. if(IsFull())不满足 || 线程被唤醒
            _q.push(in); // 生产，保证目前不为满


            // 肯定有数据
            if(_cwait_num)
            {
                // 通知
                pthread_cond_signal(&_consumer_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }

        void Pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond,&_mutex);
                _cwait_num--;

            }
            // if(IsEmpty())不满足||线程被唤醒
            *out = _q.front();
            _q.pop();
            //肯定有空间
            if(_pwait_num)
            {
                // 通知
                pthread_cond_signal(&_productor_cond); 
            }

            pthread_mutex_unlock(&_mutex);
        }

        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }

    private:
        queue<T> _q;
        int _cap;                        // 最大容量
        pthread_mutex_t _mutex;         // 互斥
        pthread_cond_t _productor_cond; // 生产者条件变量
        pthread_cond_t _consumer_cond;  // 消费者条件变量

        int _cwait_num;
        int _pwait_num;
    };
}
